package com.my.collection.performance;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

/**
 * Created by Scarlett on 2018/9/4 0004.
 */
public class Performance {
    private int[] scales = new int[]{10, 100, 1000, 10000, 100000, 1000000, 10000000};//测试数据规模，默认10，100，1000，10000，100000，1000000，10000000
    private int times = 10;//每个数据规模的测试次数，默认10次
    private Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);//并发测试线程池

    //性能测试，串行执行，可用于测试非线程安全集合类
    public <T> List<ScaleResult> test(Init<T> init, Action<T> action) {
        List<ScaleResult> scaleResults = new ArrayList<>();
        for (int scale : scales) {
            ScaleResult scaleResult = new ScaleResult();
            scaleResult.scale = scale;

            for (int i = 1; i <= this.times; i++) {
                Result result = new Result();
                result.scale = scale;
                result.time = i;

                long start = System.currentTimeMillis();
                T t = init.init();
                for (int j = 1; j <= scale; j++) {
                    action.action(t, j);
                }
                result.waste = System.currentTimeMillis() - start;
                scaleResult.results.add(result);
            }

            scaleResults.add(scaleResult);
        }
        return scaleResults;
    }

    //性能测试，并行执行，用于测试线程安全集合类
    public <T> List<ScaleResult> parallelTest(Init<T> init, Action<T> action) throws Exception {
        List<ScaleResult> scaleResults = new ArrayList<>();
        for (int scale : scales) {
            ScaleResult scaleResult = new ScaleResult();
            scaleResult.scale = scale;

            for (int i = 1; i <= this.times; i++) {
                Result result = new Result();
                result.scale = scale;
                result.time = i;

                long start = System.currentTimeMillis();
                T t = init.init();
                List<FutureTask<Void>> futureTasks = new ArrayList<>();
                for (int j = 1; j <= scale; j++) {
                    int finalJ = j;
                    FutureTask<Void> futureTask = new FutureTask<>(() -> {
                        action.action(t, finalJ);
                        return null;
                    });
                    futureTasks.add(futureTask);
                    this.executor.execute(futureTask);
                }

                for (FutureTask<Void> futureTask : futureTasks) {
                    futureTask.get();
                }

                result.waste = System.currentTimeMillis() - start;
                scaleResult.results.add(result);
            }

            scaleResults.add(scaleResult);
        }
        return scaleResults;
    }

    //格式化输出性能测试结果
    public void pretty(List<ScaleResult> scaleResults) {
        System.out.print(String.format("%-15s", "scales/times"));
        for (int i = 1; i <= this.times; i++) {
            System.out.print(String.format("%-15s", i));
        }
        System.out.println();
        for (ScaleResult scaleResult : scaleResults) {
            System.out.print(String.format("%-15s", scaleResult.scale));
            for (Result result : scaleResult.results) {
                System.out.print(String.format("%-15s", String.format("%sms", result.waste)));
            }
            System.out.println();
        }
    }

    public Performance scales(int[] scales) {
        this.scales = scales;
        return this;
    }

    public Performance times(int times) {
        this.times = times;
        return this;
    }

    public Performance executor(Executor executor) {
        this.executor = executor;
        return this;
    }

    public interface Init<T> {

        T init();
    }


    public interface Action<T> {

        void action(T t, int index);
    }


    public static class ScaleResult {
        private int scale;//数据规模
        private List<Result> results = new ArrayList<>();
    }


    public static class Result {
        private int scale;//数据规模
        private int time;//第几次测试
        private long waste;//花费时间

    }


}
